/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		win32 tc module.cpp
 * PURPOSE:		definition of win32 module class and frontend to opening win32 objects by type name
 *				support for securable object types specific to Win32
 */

namespace Win32
{

Object *OpenWin32KeyByName(PCWSTR Name, SDE_GENERIC_OPTS const &Go, UCHAR Privileges, ULONG ExtraSdFlags)
{
	auto_szpod<WCHAR> NameCopy = Name;
	SIZE_T NameLen = wcslen(Name);
	PWSTR KeySubpath = NULL; //backed by NameCopy

	for(SIZE_T i = 0; i < NameLen; i++)
		if(NameCopy[i] == L'\\')
		{
			KeySubpath = NameCopy + i + 1;
			NameCopy[i] = L'\0';
			break;
		}

	HKEY Root = NULL;

	if(_wcsicmp(NameCopy, L"HKEY_CLASSES_ROOT") == 0 || _wcsicmp(NameCopy, L"HKCR") == 0)
		Root = HKEY_CLASSES_ROOT;
	else if(_wcsicmp(NameCopy, L"HKEY_CURRENT_USER") == 0 || _wcsicmp(NameCopy, L"HKCU") == 0)
		Root = HKEY_CURRENT_USER;
	else if(_wcsicmp(NameCopy, L"HKEY_LOCAL_MACHINE") == 0 || _wcsicmp(NameCopy, L"HKLM") == 0)
		Root = HKEY_LOCAL_MACHINE;
	else if(_wcsicmp(NameCopy, L"HKEY_USERS") == 0 || _wcsicmp(NameCopy, L"HKU") == 0)
		Root = HKEY_USERS;
	else if(_wcsicmp(NameCopy, L"HKEY_PERFORMANCE_DATA") == 0)
		Root = HKEY_PERFORMANCE_DATA;
	else if(_wcsicmp(NameCopy, L"HKEY_PERFORMANCE_TEXT") == 0)
		Root = HKEY_PERFORMANCE_TEXT;
	else if(_wcsicmp(NameCopy, L"HKEY_PERFORMANCE_NLSTEXT") == 0)
		Root = HKEY_PERFORMANCE_NLSTEXT;
	else if(_wcsicmp(NameCopy, L"HKEY_CURRENT_CONFIG") == 0 || _wcsicmp(NameCopy, L"HKCC") == 0)
		Root = HKEY_CURRENT_CONFIG;
	else if(_wcsicmp(NameCopy, L"HKEY_DYN_DATA") == 0)
		Root = HKEY_DYN_DATA;
	else
		RaiseWin32Error(ERROR_NOT_FOUND);

	if(Go.Host) //remote
	{
		ULONG Result = RegConnectRegistry(Go.Host, Root, &Root);

		if(Result != ERROR_SUCCESS)
			RaiseWin32Error(Result);

		return new Win32Key(OpenWin32Key(Root, KeySubpath, Privileges), KeySubpath, ExtraSdFlags); //Win32Key::ReturnNew(OpenWin32Key(Root, KeySubpath, Privileges), KeySubpath);
	}
	else //local
		return new Ntapi::Key(c_cast<HANDLE>(OpenWin32Key(Root, KeySubpath, Privileges)), ExtraSdFlags); //Ntapi::Key::ReturnNew(c_cast<HANDLE>(OpenWin32Key(Root, KeySubpath, Privileges)));
}

Object *OpenScmOrServiceByName(PCWSTR HostName OPTIONAL, PCWSTR ServiceName OPTIONAL, BOOL ReturnService, UCHAR Privileges)
{
	SC_HANDLE ScmHandle = OpenSCManager(HostName, NULL, MAXIMUM_ALLOWED | StandardPrivilegedRights(Privileges));
	RaiseOnWin32ErrorT(ScmHandle);

	if(ReturnService)
	{
		SC_HANDLE ServiceHandle = OpenService(ScmHandle, ServiceName, MAXIMUM_ALLOWED | StandardPrivilegedRights(Privileges));
		RaiseOnWin32ErrorT(ServiceHandle);
		Service *Ret = new Service(ServiceHandle, ServiceName); // Service::ReturnNew(ServiceHandle);
		CloseServiceHandle(ScmHandle);
		return Ret;
	}
	else
		return new ServiceManager(ScmHandle, HostName);  // ServiceManager::ReturnNew(ScmHandle);
}

class Win32Module : public Module::ModuleObject
{
	auto_SDE_OPTS ModOpts;

public:
	Win32Module() //win32 has no options, no specific init for ModOpts
	{}

	PCWSTR ModuleName() override
	{
		return L"Win32";
	}

	virtual SDE_OPTS &ModuleOptions() override
	{
		return ModOpts;
	}

private:

	static int ExceptionToOpenStatus(EXCEPTION_POINTERS *ExceptionPointers, Module::OPEN_STATUS &OpenStatus)
	{
		if(ExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_WIN32_ERROR)
			switch(CodeFromExceptionRecord(ExceptionPointers->ExceptionRecord))
			{
			case ERROR_ACCESS_DENIED:
				OpenStatus |= Module::SECURITY_FAILURE | Module::OBJECT_OPEN_FAIL;
				break;

			case ERROR_FILE_NOT_FOUND:
			case ERROR_PATH_NOT_FOUND:
			case ERROR_INVALID_DRIVE:
			case ERROR_NOT_FOUND:
				OpenStatus |= Module::OBJECT_NAME_NOTFOUND;
				break;

			case ERROR_INVALID_NAME:
			case ERROR_BAD_PATHNAME:
			case ERROR_INVALID_NETNAME:
			case ERROR_INVALID_SHARENAME:
				OpenStatus |= Module::OBJECT_NAME_INVALID;
				break;

			default:
				OpenStatus |= Module::OBJECT_OPEN_FAIL;
				break;
			}

		OpenStatus |= Module::COMPLETE_FAILURE;

		return EXCEPTION_CONTINUE_SEARCH; //just passing through
	}

	void OpenResourcesGuarded(Module::OPEN_STATUS &OpenStatus, SDE_GENERIC_OPTS const &GOpts, Module::ModuleResourceDest &Res, UCHAR Privileges, BOOLEAN ObjectWanted)
	{
		if(!ObjectWanted)
		{
			OpenStatus |= Module::MODULE_IRRELEVANT;
			return;
		}

		PCWSTR Name = GOpts.ObjName;
		PCWSTR Type = GOpts.ObjType;

		ACCESS_MASK DefaultAccessRequested = MAXIMUM_ALLOWED | StandardPrivilegedRights(Privileges);

		ULONG ExtraSdFlags = 0;
		ExtraSdFlags |= Privileges & PRIV_RELABEL ? SD_FLAG_LABEL_SET_ANY : 0;

		if(GOpts.Host)
			ExtraSdFlags |= Netapi::AreLabelsSupportedRemotely(GOpts.Host);
		else
			ExtraSdFlags |= AreLabelsSupportedLocally() ? SD_FLAG_LABEL_SUPPORT : 0;

		if(!Type)
		{
			OpenStatus |= Module::TYPE_NOT_SUPPORTED;
			return;
		}
		else if(_wcsicmp(Type, L"Key") == 0)
			Res.Object(OpenWin32KeyByName(Name, GOpts, Privileges, ExtraSdFlags));
		else if(_wcsicmp(Type, L"File") == 0)
			Res.Object(new Ntapi::File(OpenWin32File(Name, Privileges, GOpts.CaseSensitiveSpecified ? GOpts.CaseSensitive : FALSE), ExtraSdFlags));
		else if(_wcsicmp(Type, L"Job") == 0)
			Res.Object(new Ntapi::ObjMgrObject(RaiseOnWin32ErrorT(OpenJobObjectW_Delayed(DefaultAccessRequested, FALSE, Name)), Ntapi::Specific::Job, ExtraSdFlags));
		else if(_wcsicmp(Type, L"Section") == 0 || _wcsicmp(Type, L"FileMapping") == 0)
			Res.Object(new Ntapi::ObjMgrObject(RaiseOnWin32ErrorT(OpenFileMapping(DefaultAccessRequested, FALSE, Name)), Ntapi::Specific::Section, ExtraSdFlags));
		else if(_wcsicmp(Type, L"Event") == 0)
			Res.Object(new Ntapi::ObjMgrObject(RaiseOnWin32ErrorT(OpenEvent(DefaultAccessRequested, FALSE, Name)), Ntapi::Specific::QueryModify, ExtraSdFlags));
		else if(_wcsicmp(Type, L"Semaphore") == 0)
			Res.Object(new Ntapi::ObjMgrObject(RaiseOnWin32ErrorT(OpenSemaphore(DefaultAccessRequested, FALSE, Name)), Ntapi::Specific::QueryModify, ExtraSdFlags));
		else if(_wcsicmp(Type, L"Mutex") == 0)
			Res.Object(new Ntapi::ObjMgrObject(RaiseOnWin32ErrorT(OpenMutex(DefaultAccessRequested, FALSE, Name)), Ntapi::Specific::QueryModify, ExtraSdFlags));
		else if(_wcsicmp(Type, L"Process") == 0)
			Res.Object(new Ntapi::ObjMgrObject(RaiseOnWin32ErrorT(OpenProcess(DefaultAccessRequested, FALSE, _wtoi(Name))), Ntapi::Specific::Process, ExtraSdFlags));
		else if(_wcsicmp(Type, L"Thread") == 0)
			Res.Object(new Ntapi::ObjMgrObject(RaiseOnWin32ErrorT(OpenThread_Delayed(DefaultAccessRequested, FALSE, _wtoi(Name))), Ntapi::Specific::Thread, ExtraSdFlags));
		else if(_wcsicmp(Type, L"Desktop") == 0)
			Res.Object(ReturnNewDesktop(RaiseOnWin32ErrorT(OpenDesktop(Name, FALSE, 0, DefaultAccessRequested)), ExtraSdFlags));
		else if(_wcsicmp(Type, L"WindowStation") == 0)
			Res.Object(ReturnNewWindowStation(RaiseOnWin32ErrorT(OpenWindowStation(Name, FALSE, DefaultAccessRequested)), ExtraSdFlags));
		else if(_wcsicmp(Type, L"ServiceManager") == 0)
			Res.Object(OpenScmOrServiceByName(GOpts.Host, NULL, FALSE, Privileges));
		else if(_wcsicmp(Type, L"Service") == 0)
			Res.Object(OpenScmOrServiceByName(GOpts.Host, Name, TRUE, Privileges));
		else if(_wcsicmp(Type, L"Printer") == 0)
			Res.Object(new NamedSecObject(Name, SE_PRINTER, Privileges));
		else if(_wcsicmp(Type, L"WmiGuid") == 0)
			Res.Object(new NamedSecObject(Name, SE_WMIGUID_OBJECT, Privileges));
		//else if(_wcsicmp(Type, L"Ntds") == 0)
		//	Res.Object(new NamedSecObject(Name, SE_DS_OBJECT_ALL, Privileges));
		//else if(_wcsicmp(Type, L"Ntds") == 0)
		//	Res.Object(new NamedSecObject(Name, SE_DS_OBJECT, Privileges);
		//else if(_wcsicmp(Type, L"Ntds") == 0)
		//	Res.Object(new NtdsObject(Name);
		else
		{
			OpenStatus |= Module::TYPE_NOT_SUPPORTED;
			return;
		}

		OpenStatus |= Module::OBJECT_OPENED;
	}

public:
	virtual void OpenResources(Module::OPEN_STATUS &OpenStatus, SDE_GENERIC_OPTS const &GOpts, Module::ModuleResourceDest &Res, UCHAR Privileges, BOOLEAN ObjectWanted) override
	{
		__try
		{
			OpenResourcesGuarded(OpenStatus, GOpts, Res, Privileges, ObjectWanted);
		}
		__except(ExceptionToOpenStatus(exception_info(), OpenStatus))
		{}
	}
};

}//namespace Win32